﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using AmazonDynamoDBStub.EntityFramework;
using AmazonDynamoDBStub.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AmazonDynamoDBStub
{
    [TestClass]
    public class AmazonDynamoDBStubTest
    {
        private const string TableName = "TestTable";
        private const string HashKey = "HashKey";
        private const string RangeKey = "RangeKey";

        private AmazonDynamoDB client;

        [TestInitialize]
        public void Initialize()
        {
#if Amazon
            client = AWSClientFactory.CreateAmazonDynamoDBv2Client(RegionEndpoint.APSoutheast1);
            ResetTable();
#endif

#if SQL
            client = AmazonDynamoDBStubFactory.CreateAmazonDynamoDBv2Client();
            RemoveTable();
#endif
        }

        [TestCleanup]
        public void Cleanup()
        {
#if Amazon
            ResetTable();
#endif

#if SQL
            RemoveTable();
#endif
        }

        [Conditional("Amazon")]
        private void WaitForCreating()
        {
            DescribeTableRequest describeTableRequest = new DescribeTableRequest();
            describeTableRequest.TableName = TableName;

            while (client.DescribeTable(describeTableRequest).DescribeTableResult.Table.TableStatus == "CREATING")
            {
                Thread.Sleep(1000);
            }
        }

        [Conditional("Amazon")]
        private void WaitForDeleting()
        {
            try
            {
                DescribeTableRequest describeTableRequest = new DescribeTableRequest();
                describeTableRequest.TableName = TableName;

                while (client.DescribeTable(describeTableRequest).DescribeTableResult.Table.TableStatus == "DELETING")
                {
                    Thread.Sleep(1000);
                }
            }
            catch (ResourceNotFoundException)
            {
                return;
            }
        }

        private void InitializeTable()
        {
            if (!client.ListTables().ListTablesResult.TableNames.Contains(TableName))
            {
                CreateTableRequest request = new CreateTableRequest();
                request.TableName = TableName;
                request.AttributeDefinitions = new List<AttributeDefinition>
                {
                    new AttributeDefinition { AttributeName = HashKey, AttributeType = "S" },
                    new AttributeDefinition { AttributeName = RangeKey, AttributeType = "S" }
                };
                request.KeySchema = new List<KeySchemaElement>
                {
                    new KeySchemaElement { AttributeName = HashKey, KeyType = "HASH" },
                    new KeySchemaElement { AttributeName = RangeKey, KeyType = "RANGE" }
                };
                request.ProvisionedThroughput = new ProvisionedThroughput
                {
                    ReadCapacityUnits = 3,
                    WriteCapacityUnits = 5
                };

                var response = client.CreateTable(request);

                WaitForCreating();
            }
        }

        private void InitializeTableForCreating()
        {
            RemoveTable();
        }

        private void RemoveTable()
        {
            if (client.ListTables().ListTablesResult.TableNames.Contains(TableName))
            {
                DeleteTableRequest request = new DeleteTableRequest();
                request.TableName = TableName;

                client.DeleteTable(request);

                WaitForDeleting();
            }
        }

        private void ResetTable()
        {
            if (client.ListTables().ListTablesResult.TableNames.Contains(TableName))
            {
                if (!VerifyTable())
                {
                    RemoveTable();
                    InitializeTable();
                }
                else
                    RemoveDataIfExists();
            }
            else
                InitializeTable();
        }

        private bool VerifyTable()
        {
            DescribeTableRequest request = new DescribeTableRequest();
            request.TableName = TableName;

            var response = client.DescribeTable(request);

            return response.DescribeTableResult.Table.KeySchema.Exists(key => key.AttributeName == HashKey && key.KeyType == "HASH") &&
                response.DescribeTableResult.Table.KeySchema.Exists(key => key.AttributeName == RangeKey && key.KeyType == "RANGE");
        }

        private void RemoveDataIfExists()
        {
            ScanRequest scanRequest = new ScanRequest();
            scanRequest.TableName = TableName;
            var scanResponse = client.Scan(scanRequest);

            if (scanResponse.ScanResult.Items.Count > 0)
            {
                var writeRequests = new List<WriteRequest>();
                foreach (var item in scanResponse.ScanResult.Items)
                {
                    var writeRequest = new WriteRequest();
                    writeRequest.DeleteRequest = new DeleteRequest();
                    writeRequest.DeleteRequest.Key.Add(HashKey, item[HashKey]);
                    writeRequest.DeleteRequest.Key.Add(RangeKey, item[RangeKey]);
                    writeRequests.Add(writeRequest);
                }

                BatchWriteItemRequest batchWriteItemRequest = new BatchWriteItemRequest();
                batchWriteItemRequest.RequestItems.Add(TableName, writeRequests);

                client.BatchWriteItem(batchWriteItemRequest);
            }
        }

        #region CreateTable

        [TestMethod]
        [TestCategory("CreateTable")]
        public void CreateTable()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            request.TableName = TableName;
            request.AttributeDefinitions = new List<AttributeDefinition>
            {
                new AttributeDefinition { AttributeName = HashKey, AttributeType = "S" },
                new AttributeDefinition { AttributeName = RangeKey, AttributeType = "S" }
            };
            request.KeySchema = new List<KeySchemaElement>
            {
                new KeySchemaElement { AttributeName = HashKey, KeyType = "HASH" },
                new KeySchemaElement { AttributeName = RangeKey, KeyType = "RANGE" }
            };
            request.ProvisionedThroughput = new ProvisionedThroughput
            {
                ReadCapacityUnits = 3,
                WriteCapacityUnits = 5
            };

            var response = client.CreateTable(request);

            WaitForCreating();
        }

        [TestMethod]
        [TestCategory("CreateTable")]
        public void CreateTableWithOnlyHashKey()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            request.TableName = TableName;
            request.AttributeDefinitions = new List<AttributeDefinition>
            {
                new AttributeDefinition { AttributeName = HashKey, AttributeType = "S" },
            };
            request.KeySchema = new List<KeySchemaElement>
            {
                new KeySchemaElement { AttributeName = HashKey, KeyType = "HASH" },
            };
            request.ProvisionedThroughput = new ProvisionedThroughput
            {
                ReadCapacityUnits = 3,
                WriteCapacityUnits = 5
            };

            var response = client.CreateTable(request);

            WaitForCreating();
        }

        [TestMethod]
        [TestCategory("CreateTable")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void CreateTableWithEmptyTableName()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            var response = client.CreateTable(request);
        }

        [TestMethod]
        [TestCategory("CreateTable")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void CreateTableWithEmptyAttributeDefinitions()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            request.TableName = TableName;
            request.KeySchema = new List<KeySchemaElement>
            {
                new KeySchemaElement { AttributeName = HashKey, KeyType = "HASH" },
                new KeySchemaElement { AttributeName = RangeKey, KeyType = "RANGE" }
            };
            request.ProvisionedThroughput = new ProvisionedThroughput
            {
                ReadCapacityUnits = 3,
                WriteCapacityUnits = 5
            };

            var response = client.CreateTable(request);
        }

        [TestMethod]
        [TestCategory("CreateTable")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void CreateTableWithEmptyKeySchema()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            request.TableName = TableName;
            request.AttributeDefinitions = new List<AttributeDefinition>
            {
                new AttributeDefinition { AttributeName = HashKey, AttributeType = "S" },
                new AttributeDefinition { AttributeName = RangeKey, AttributeType = "S" }
            };
            request.ProvisionedThroughput = new ProvisionedThroughput
            {
                ReadCapacityUnits = 3,
                WriteCapacityUnits = 5
            };

            var response = client.CreateTable(request);
        }

        [TestMethod]
        [TestCategory("CreateTable")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void CreateTableWithEmptyProvisionedThroughput()
        {
            InitializeTableForCreating();

            CreateTableRequest request = new CreateTableRequest();
            request.TableName = TableName;
            request.AttributeDefinitions = new List<AttributeDefinition>
            {
                new AttributeDefinition { AttributeName = HashKey, AttributeType = "S" },
                new AttributeDefinition { AttributeName = RangeKey, AttributeType = "S" }
            };
            request.KeySchema = new List<KeySchemaElement>
            {
                new KeySchemaElement { AttributeName = HashKey, KeyType = "HASH" },
                new KeySchemaElement { AttributeName = RangeKey, KeyType = "RANGE" }
            };

            var response = client.CreateTable(request);
        }

        #endregion

        #region DescribeTable

        [TestMethod]
        [TestCategory("DescribeTable")]
        public void DescribeTable()
        {
            InitializeTable();

            DescribeTableRequest request = new DescribeTableRequest();
            request.TableName = TableName;

            var response = client.DescribeTable(request);

            Assert.AreEqual(HashKey, response.DescribeTableResult.Table.KeySchema[0].AttributeName);
            Assert.AreEqual("HASH", response.DescribeTableResult.Table.KeySchema[0].KeyType);
            Assert.AreEqual(RangeKey, response.DescribeTableResult.Table.KeySchema[1].AttributeName);
            Assert.AreEqual("RANGE", response.DescribeTableResult.Table.KeySchema[1].KeyType);
            Assert.AreEqual("ACTIVE", response.DescribeTableResult.Table.TableStatus);
        }

        #endregion

        #region DeleteItem

        [TestMethod]
        [TestCategory("DeleteItem")]
        public void DeleteItem()
        {
            PutItem();

            DeleteItemRequest request = new DeleteItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var response = client.DeleteItem(request);
        }

        [TestMethod]
        [TestCategory("DeleteItem")]
        [ExpectedException(typeof(ResourceNotFoundException))]
        public void DeleteItemOfTableWhichDoesNotExist()
        {
            PutItem();

            DeleteItemRequest request = new DeleteItemRequest();
            request.TableName = "NotExisting";
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var response = client.DeleteItem(request);
        }

        [TestMethod]
        [TestCategory("DeleteItem")]
        public void DeleteItemWhichDoesNotExist()
        {
            PutItem();

            DeleteItemRequest request = new DeleteItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "111" });

            var response = client.DeleteItem(request);
        }

        #endregion

        #region DeleteTable

        [TestMethod]
        [TestCategory("DeleteTable")]
        public void DeleteTable()
        {
            InitializeTable();

            DeleteTableRequest request = new DeleteTableRequest();
            request.TableName = TableName;

            var response = client.DeleteTable(request);

            WaitForDeleting();

            Assert.AreEqual(0, client.ListTables().ListTablesResult.TableNames.Count);
        }

        [TestMethod]
        [TestCategory("DeleteTable")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void DeleteTableWithEmptyTableName()
        {
            InitializeTable();

            DeleteTableRequest request = new DeleteTableRequest();

            var response = client.DeleteTable(request);
        }

        [TestMethod]
        [TestCategory("DeleteTable")]
        [ExpectedException(typeof(ResourceNotFoundException))]
        public void DeleteTableWhichDoesNotExist()
        {
            InitializeTableForCreating();

            DeleteTableRequest request = new DeleteTableRequest();
            request.TableName = TableName;

            var response = client.DeleteTable(request);
        }

        #endregion

        #region PutItem

        [TestMethod]
        [TestCategory("PutItem")]
        public void PutItem()
        {
            InitializeTable();

            PutItemRequest request = new PutItemRequest();
            request.TableName = TableName;
            request.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            var response = client.PutItem(request);
        }

        [TestMethod]
        [TestCategory("PutItem")]
        public void PutItemWithOnlyHashKey()
        {
            CreateTableWithOnlyHashKey();

            PutItemRequest request = new PutItemRequest();
            request.TableName = TableName;
            request.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Item.Add("TestAttribute", new AttributeValue { S = "BBB" });

            var response = client.PutItem(request);
        }

        [TestMethod]
        [TestCategory("PutItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void PutItemWithEmptyTableName()
        {
            InitializeTable();

            PutItemRequest request = new PutItemRequest();
            request.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            var response = client.PutItem(request);
        }

        [TestMethod]
        [TestCategory("PutItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void PutItemWithEmptyItem()
        {
            InitializeTable();

            PutItemRequest request = new PutItemRequest();
            request.TableName = TableName;

            var response = client.PutItem(request);
        }

        [TestMethod]
        [TestCategory("PutItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void PutItemWithoutHashKeyAssigned()
        {
            InitializeTable();

            PutItemRequest request = new PutItemRequest();
            request.TableName = TableName;
            request.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            var response = client.PutItem(request);
        }

        [TestMethod]
        [TestCategory("PutItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void PutItemWithEmptyStringValueAssigned()
        {
            InitializeTable();

            PutItemRequest request = new PutItemRequest();
            request.TableName = TableName;
            request.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.Item.Add("TestAttribute", new AttributeValue { S = "" });

            var response = client.PutItem(request);
        }

        #endregion

        #region UpdateItem

        [TestMethod]
        [TestCategory("UpdateItem")]
        public void UpdateNewItem()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute1", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "CCC" } });
            request.AttributeUpdates.Add("TestAttribute2", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "DDD" } });

            var response = client.UpdateItem(request);

            GetItemRequest getItemRequest = new GetItemRequest();
            getItemRequest.TableName = TableName;
            getItemRequest.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            getItemRequest.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var getItemResponse = client.GetItem(getItemRequest);

            Assert.AreEqual(4, getItemResponse.GetItemResult.Item.Count);
            Assert.AreEqual("CCC", getItemResponse.GetItemResult.Item["TestAttribute1"].S);
            Assert.AreEqual("DDD", getItemResponse.GetItemResult.Item["TestAttribute2"].S);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        public void UpdateExistingItem()
        {
            UpdateNewItem();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute1", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "CCC" } });
            request.AttributeUpdates.Add("TestAttribute2", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "EEE" } });

            var response = client.UpdateItem(request);

            GetItemRequest getItemRequest = new GetItemRequest();
            getItemRequest.TableName = TableName;
            getItemRequest.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            getItemRequest.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var getItemResponse = client.GetItem(getItemRequest);

            Assert.AreEqual(4, getItemResponse.GetItemResult.Item.Count);
            Assert.AreEqual("CCC", getItemResponse.GetItemResult.Item["TestAttribute1"].S);
            Assert.AreEqual("EEE", getItemResponse.GetItemResult.Item["TestAttribute2"].S);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithInvalidHashKeyAttributeTypeAssigned()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { N = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "CCC" } });

            var response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithInvalidRangeKeyAttributeTypeAssigned()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { N = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "CCC" } });

            var response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithEmptyAttributeValueToUpdate()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "" } });

            var response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithInvalidAction()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "InvalidAction", Value = new AttributeValue { S = "CCC" } });

            var response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        public void UpdateItemWithAddActionIfNoItemIsFound()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "ADD", Value = new AttributeValue { N = "1" } });

            var response = client.UpdateItem(request);

            GetItemRequest getItemRequest = new GetItemRequest();
            getItemRequest.TableName = TableName;
            getItemRequest.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            getItemRequest.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var getItemResponse = client.GetItem(getItemRequest);

            Assert.AreEqual("1", getItemResponse.GetItemResult.Item["TestAttribute"].N);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithAddActionAndInvalidAttributeTypeIfNoItemIsFound()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "ADD", Value = new AttributeValue { S = "CCC" } });

            var response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithAddActionAndExistingAttributeIsOfStringType()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { S = "CCC" } });

            var response = client.UpdateItem(request);

            request.AttributeUpdates["TestAttribute"] = new AttributeValueUpdate { Action = "ADD", Value = new AttributeValue { N = "1" } };

            response = client.UpdateItem(request);
        }

        [TestMethod]
        [TestCategory("UpdateItem")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void UpdateItemWithAddActionAndTheAttributeToBeAddedIsOfStringType()
        {
            InitializeTable();

            UpdateItemRequest request = new UpdateItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });
            request.AttributeUpdates.Add("TestAttribute", new AttributeValueUpdate { Action = "PUT", Value = new AttributeValue { N = "1" } });

            var response = client.UpdateItem(request);

            request.AttributeUpdates["TestAttribute"] = new AttributeValueUpdate { Action = "ADD", Value = new AttributeValue { S = "CCC" } };

            response = client.UpdateItem(request);
        }

        #endregion

        #region GetItem

        [TestMethod]
        [TestCategory("GetItem")]
        public void GetItem()
        {
            PutItem();

            GetItemRequest request = new GetItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var response = client.GetItem(request);

            Assert.AreEqual(3, response.GetItemResult.Item.Count);
            Assert.AreEqual("AAA", response.GetItemResult.Item[HashKey].S);
            Assert.AreEqual("BBB", response.GetItemResult.Item[RangeKey].S);
        }

        [TestMethod]
        [TestCategory("GetItem")]
        public void GetItemWithoutRangeKeyConditionAssigned()
        {
            PutItemWithOnlyHashKey();

            GetItemRequest request = new GetItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });

            var getItemResponse = client.GetItem(request);

            Assert.AreEqual(2, getItemResponse.GetItemResult.Item.Count);
            Assert.AreEqual("AAA", getItemResponse.GetItemResult.Item[HashKey].S);
        }

        [TestMethod]
        [TestCategory("GetItem")]
        public void GetItemReturnsNothing()
        {
            InitializeTable();

            GetItemRequest request = new GetItemRequest();
            request.TableName = TableName;
            request.Key.Add(HashKey, new AttributeValue { S = "AAA" });
            request.Key.Add(RangeKey, new AttributeValue { S = "BBB" });

            var response = client.GetItem(request);

            Assert.IsNull(response.GetItemResult.Item);
        }

        #endregion

        #region Query

        [TestMethod]
        [TestCategory("Query")]
        public void Query()
        {
            PutItem();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "EQ" });
            request.KeyConditions.Add(RangeKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "BBB" } }, ComparisonOperator = "EQ" });

            var response = client.Query(request);

            Assert.AreEqual(1, response.QueryResult.Count);
            Assert.IsNotNull(response.QueryResult.Items.SingleOrDefault(item => item[HashKey].S == "AAA" && item[RangeKey].S == "BBB"));
        }

        [TestMethod]
        [TestCategory("Query")]
        public void QueryWithoutRangeKeyConditionAssigned()
        {
            PutItem();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "EQ" });

            var response = client.Query(request);

            Assert.AreEqual(1, response.QueryResult.Count);
            Assert.IsNotNull(response.QueryResult.Items.SingleOrDefault(item => item[HashKey].S == "AAA"));
        }

        [TestMethod]
        [TestCategory("Query")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void QueryItemsWithOnlyHashKey()
        {
            PutItemWithOnlyHashKey();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "EQ" });

            var response = client.Query(request);
        }

        [TestMethod]
        [TestCategory("Query")]
        public void QueryReturnsNothing()
        {
            PutItem();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "EQ" });
            request.KeyConditions.Add(RangeKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "NotExisting" } }, ComparisonOperator = "EQ" });

            var response = client.Query(request);

            Assert.AreEqual(0, response.QueryResult.Count);
        }

        [TestMethod]
        [TestCategory("Query")]
        public void QueryBeginsWith()
        {
            PutItem();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "EQ" });
            request.KeyConditions.Add(RangeKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "B" } }, ComparisonOperator = "BEGINS_WITH" });

            var response = client.Query(request);

            Assert.AreEqual(1, response.QueryResult.Count);
            Assert.IsNotNull(response.QueryResult.Items.SingleOrDefault(item => item[HashKey].S == "AAA" && item[RangeKey].S == "BBB"));
        }

        [TestMethod]
        [TestCategory("Query")]
        [ExpectedException(typeof(AmazonDynamoDBException))]
        public void QueryWithInvalidComparisonOperator()
        {
            PutItem();

            QueryRequest request = new QueryRequest();
            request.TableName = TableName;
            request.KeyConditions.Add(HashKey, new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "AAA" } }, ComparisonOperator = "InvalidComparisonOperator" });

            var response = client.Query(request);
        }

        #endregion

        #region Scan

        [TestMethod]
        [TestCategory("Scan")]
        public void Scan()
        {
            PutItem();

            ScanRequest request = new ScanRequest();
            request.TableName = TableName;

            var response = client.Scan(request);

            Assert.AreEqual(1, response.ScanResult.Count);
            Assert.IsNotNull(response.ScanResult.Items.SingleOrDefault(item => item[HashKey].S == "AAA" && item[RangeKey].S == "BBB"));
        }

        [TestMethod]
        [TestCategory("Scan")]
        public void ScanForMultipleItems()
        {
            InitializeTable();

            PutItemRequest putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            client.PutItem(putItemRequest);

            putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "DDD" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "EEE" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { N = "333" });

            client.PutItem(putItemRequest);

            ScanRequest request = new ScanRequest();
            request.TableName = TableName;

            var response = client.Scan(request);

            Assert.AreEqual(2, response.ScanResult.Count);
        }

        [TestMethod]
        [TestCategory("Scan")]
        public void ScanWithFilterAssigned()
        {
            InitializeTable();

            PutItemRequest putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            client.PutItem(putItemRequest);

            putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "DDD" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "EEE" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { N = "333" });

            client.PutItem(putItemRequest);

            ScanRequest request = new ScanRequest();
            request.TableName = TableName;
            request.ScanFilter.Add("TestAttribute", new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { N = "333" } }, ComparisonOperator = "EQ" });

            var response = client.Scan(request);

            Assert.AreEqual(1, response.ScanResult.Count);
            Assert.IsNotNull(response.ScanResult.Items.SingleOrDefault(item => item[HashKey].S == "DDD" && item[RangeKey].S == "EEE"));
        }

        [TestMethod]
        [TestCategory("Scan")]
        public void ScanWithFilterAssignedWithWrongAttributeType()
        {
            InitializeTable();

            PutItemRequest putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "AAA" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "BBB" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { S = "CCC" });

            client.PutItem(putItemRequest);

            putItemRequest = new PutItemRequest();
            putItemRequest.TableName = TableName;
            putItemRequest.Item.Add(HashKey, new AttributeValue { S = "DDD" });
            putItemRequest.Item.Add(RangeKey, new AttributeValue { S = "EEE" });
            putItemRequest.Item.Add("TestAttribute", new AttributeValue { N = "333" });

            client.PutItem(putItemRequest);

            ScanRequest request = new ScanRequest();
            request.TableName = TableName;
            request.ScanFilter.Add("TestAttribute", new Condition { AttributeValueList = new List<AttributeValue> { new AttributeValue { S = "333" } }, ComparisonOperator = "EQ" });

            var response = client.Scan(request);

            Assert.AreEqual(0, response.ScanResult.Count);
        }

        #endregion
    }
}
